#include "ltdc.h"

static int lcd_setcolreg(unsigned regno, unsigned red, 
						unsigned green, unsigned blue, 
						unsigned transp, struct fb_info *info);

static int ltdc_probe(struct platform_device *pdev);
static int ltdc_remove(struct platform_device *pdev);

static struct ltdc_handle ltdc_handle[LTDC_NUMBER];

/* 匹配列表，用于设备树和平台驱动匹配 */
static const struct of_device_id ltdc_of_match[] = {
	{.compatible = "atk,ltdc"},
	{ /* Sentinel */ }
};
//平台驱动
static struct platform_driver ltdc_drv = {
	.driver = {
		.name = "atk,ltdc",
		.owner = THIS_MODULE,
		.pm = NULL,
		.of_match_table = ltdc_of_match,
	},
	.probe = ltdc_probe,
	.remove = ltdc_remove,
};

static struct fb_ops lcd_ops = {
	.owner = THIS_MODULE,
	.fb_setcolreg = lcd_setcolreg,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
	chan &= 0xffff;
	chan >>= 16 - bf->length;
	return chan << bf->offset;
}

static int lcd_setcolreg(unsigned regno, unsigned red, 
						unsigned green, unsigned blue, 
						unsigned transp, struct fb_info *info)
{
	unsigned int val;
	unsigned int *pseudo_palette;
	
	if (regno >= 16)
		return -EINVAL;

	val  = chan_to_field(red, &info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue, &info->var.blue);

	pseudo_palette = info->pseudo_palette;
	pseudo_palette[regno] = val;
	return 0;
}

static void ltdc_init(ltdc_register_t *lcdif, struct display_timing *dt, int fb_bpp, unsigned int fb_phy)
{
	uint32_t val;

	//[27:16]HSYNC宽度HSW - 1，[11:0]VSYNC宽度VSW - 1
	val = ((dt->hsync_len.typ - 1) << 16) | ((dt->vsync_len.typ - 1) << 0);
	writel(val, &lcdif->SSCR);
	//[27:16]HSYNC宽度HSW + 左黑框HBP - 1，[11:0]VSYNC宽度VSW + 上黑框VBP - 1
	val = ((dt->hsync_len.typ + dt->hback_porch.typ - 1) << 16) | \
		((dt->vsync_len.typ + dt->vback_porch.typ - 1) << 0);
	writel(val, &lcdif->BPCR);
	//[27:16]HSYNC宽度HSW + 左黑框HBP + 水平有效高度 - 1，[11:0]VSYNC宽度VSW + 上黑框VBP + 垂直有效高度 - 1
	val = ((dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ - 1) << 16) | \
		((dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ - 1) << 0);
	writel(val, &lcdif->AWCR);
	//[27:16]HSYNC宽度HSW + 左黑框HBP + 水平有效高度 + 右黑框HFP - 1，[11:0]VSYNC宽度VSW + 上黑框VBP + 垂直有效高度 + 下黑框VFP - 1
	val = ((dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ + dt->hfront_porch.typ - 1) << 16) | \
		((dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ + dt->vfront_porch.typ - 1) << 0);
	writel(val, &lcdif->TWCR);
	//[31]水平同步极性，[30]垂直同步极性，[29]非数据使能极性，[28]像素时钟极性
	val = readl(&lcdif->GCR);
	val &= ~(0xF << 28);
	if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
		val |= 0x01 << 28;
	if (dt->flags & DISPLAY_FLAGS_DE_HIGH)
		val |= 0x01 << 29;
	if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
		val |= 0x01 << 30;
	if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
		val |= 0x01 << 31;
	writel(val, &lcdif->GCR);
	//[23:16]默认颜色红色，[15:8]默认颜色绿色，[7:0]默认颜色蓝色
	val = 0x000000;
	writel(val, &lcdif->BCCR);

	//[27:16]窗口水平结束位置，窗口的一行的最后一个可见像素，[11:0]窗口水平起始位置，窗口的一行的第一个可见像素
	val = ((dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ - 1) << 16) | \
		((dt->hsync_len.typ + dt->hback_porch.typ) << 0);
	writel(val, &lcdif->L1WHPCR);
	//[27:16]窗口垂直结束位置，窗口的最后一个可见行，[11:0]窗口垂直起始位置，窗口的第一个可见行
	val = ((dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ - 1) << 16) | \
		((dt->vsync_len.typ + dt->vback_porch.typ) << 0);
	writel(val, &lcdif->L1WVPCR);
	//[29:16]素某行的起始处到下一行的起始处的增量，[13:0]义一行像素的长度 + 7
	val = ((dt->hactive.typ * (fb_bpp >> 3)) << 16) | \
		(dt->hactive.typ * (fb_bpp >> 3) + 7);
	writel(val, &lcdif->L1CFBLR);
	//[10:0]显存总共的行数
	val = dt->vactive.typ;
	writel(val, &lcdif->L1CFBLNR);
	//[31:24]默认 Alpha 值，[23:16]默认颜色红色，[15:8]默认颜色绿色，[7:0]默认颜色蓝色
	val = 0x00000000;
	writel(val, &lcdif->L1DCCR);
	//[7:0]透明度常数
	val = 0xff;
	writel(val, &lcdif->L1CACR);
	/*
	 * [10:8]BF1，[2:0]BF2
	 * BC = BF1 x C + BF2 x Cs 
	 * BF1设置为100：constant alpha即LTDC_L1CACR设置的值0xff，表示完全不透明
	 * BF2设置为101：constant alpha即LTDC_L1CACR设置的值0xff，表示完全不透明
	 * C为当前层的颜色,
	 * Cs为背景色，不设置，默认值为0，即黑色
	 */
	val = (4 << 8) | (5 << 0);
	writel(val, &lcdif->L1BFCR);
	//[2:0]像素格式
	switch(fb_bpp) {
	case 16:
		//RGB565
		val = 0x2;
		break;
	case 24:
		//RGB888
		val = 0x1;
		break;
	case 32:
		//ARGB8888
		val = 0x0;
		break;
	default:
		val = 0x2;
		break;
	}
	writel(val, &lcdif->L1PFCR);
	//设置显存地址
	val = fb_phy;
	writel(val, &lcdif->L1CFBAR);
}

static void ltdc_enable(ltdc_register_t *lcdif)
{
	uint32_t val;

	//[0]使能 layer1
	val = readl(&lcdif->L1CR);
	val |= 0x01;
	writel(val, &lcdif->L1CR);
	//[0]立即重载 layer1 和 layer2 的参数
	val = readl(&lcdif->SRCR);
	val |= 0x01;
	writel(val, &lcdif->SRCR);
	//[0]使能LTDC控制器
	val = readl(&lcdif->GCR);
	val |= 0x01;
	writel(val, &lcdif->GCR);
}

static void ltdc_disable(ltdc_register_t *lcdif)
{
	uint32_t val;

	//[0]禁止 layer1
	val = readl(&lcdif->L1CR);
	val &= ~0x01;
	writel(val, &lcdif->L1CR);
	//[0]禁止LTDC控制器
	val = readl(&lcdif->GCR);
	val &= ~0x01;
	writel(val, &lcdif->GCR);
}

//设备和驱动匹配成功执行
static int ltdc_probe(struct platform_device *pdev)
{
	int result;
	uint32_t id;
	struct resource *resource;
	struct device_node *display_np;
	struct display_timings *timings;

	printk("%s\r\n", __FUNCTION__);

	//获取设备ID号
	result = of_property_read_u32(pdev->dev.of_node, "id", &id);
	if(result != 0)
	{
		printk("get dev id failed\r\n");
		return result;
	}
	if(id >= LTDC_NUMBER)
	{
		printk("dev id invalid\r\n");
		return -EINVAL;
	}

	//检查是否占用
	if(ltdc_handle[id].use_flag) 
	{
		printk("dev id repetition\r\n");
		return -EINVAL;
	}
	pdev->id = id;

	//设置设备的DMA寻址范围
	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);

	//标记为已占用
	ltdc_handle[id].use_flag = 1;

	//获取时序参数
	display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
	if(!display_np)
	{
		ltdc_handle[id].use_flag = 0;
		printk("parse display node failed\r\n");
		return -EINVAL;
	}
	result = of_property_read_u32(display_np, "bus-width", &ltdc_handle[id].bus_width);
	if(result != 0)
	{
		ltdc_handle[id].use_flag = 0;
		printk("get bus-width failed\r\n");
		return result;
	}
	result = of_property_read_u32(display_np, "bits-per-pixel", &ltdc_handle[id].bits_per_pixel);
	if(result != 0)
	{
		ltdc_handle[id].use_flag = 0;
		printk("get bits-per-pixel failed\r\n");
		return result;
	}
	timings = of_get_display_timings(display_np);
	if(!timings)
	{
		ltdc_handle[id].use_flag = 0;
		printk("get display timings failed\r\n");
		return -EINVAL;
	}
	ltdc_handle[id].timing = timings->timings[timings->native_mode];

	//分配显存
	ltdc_handle[id].fb_size = ltdc_handle[id].timing->hactive.typ * ltdc_handle[id].timing->vactive.typ * ltdc_handle[id].bits_per_pixel / 8;
	ltdc_handle[id].fb_buffer = dma_alloc_coherent(&pdev->dev, ltdc_handle[id].fb_size, &ltdc_handle[id].fb_phy, GFP_KERNEL);
	if(!ltdc_handle[id].fb_buffer)
	{
		ltdc_handle[id].use_flag = 0;
		printk("alloc fb_buffer failed\r\n");
		return -ENOMEM;
	}

	/* 分配 struct fb_info */
	ltdc_handle[id].fb_info = framebuffer_alloc(0, &pdev->dev);
	if(!ltdc_handle[id].fb_info)
	{
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("alloc fb_info failed\r\n");
		return -ENOMEM;
	}

	/* 初始化 struct fb_info */
	//显存虚拟地址和大小
	ltdc_handle[id].fb_info->screen_base = ltdc_handle[id].fb_buffer;
	ltdc_handle[id].fb_info->screen_size = ltdc_handle[id].fb_size;
	//底层操作函数集合
	ltdc_handle[id].fb_info->fbops = &lcd_ops;
	//颜色表
	ltdc_handle[id].fb_info->pseudo_palette = ltdc_handle[id].pseudo_palette;
	//LCD分辨率
	ltdc_handle[id].fb_info->var.xres = ltdc_handle[id].timing->hactive.typ;
	ltdc_handle[id].fb_info->var.yres = ltdc_handle[id].timing->vactive.typ;
	ltdc_handle[id].fb_info->var.xres_virtual = ltdc_handle[id].timing->hactive.typ;
	ltdc_handle[id].fb_info->var.yres_virtual = ltdc_handle[id].timing->vactive.typ;
	//像素格式,
	ltdc_handle[id].fb_info->var.bits_per_pixel = ltdc_handle[id].bits_per_pixel;
	if(ltdc_handle[id].bits_per_pixel == 16)
	{
		//RGB565
		ltdc_handle[id].fb_info->var.red.offset = 11;
		ltdc_handle[id].fb_info->var.red.length = 5;
		ltdc_handle[id].fb_info->var.green.offset = 5;
		ltdc_handle[id].fb_info->var.green.length = 6;
		ltdc_handle[id].fb_info->var.blue.offset = 0;
		ltdc_handle[id].fb_info->var.blue.length = 5;
	}
	else if(ltdc_handle[id].bits_per_pixel == 24)
	{
		//RGB888
		ltdc_handle[id].fb_info->var.red.offset = 16;
		ltdc_handle[id].fb_info->var.red.length = 8;
		ltdc_handle[id].fb_info->var.green.offset = 8;
		ltdc_handle[id].fb_info->var.green.length = 8;
		ltdc_handle[id].fb_info->var.blue.offset = 0;
		ltdc_handle[id].fb_info->var.blue.length = 8;
	}
	else if(ltdc_handle[id].bits_per_pixel == 32)
	{
		//ARGB8888
		ltdc_handle[id].fb_info->var.transp.offset = 24;
		ltdc_handle[id].fb_info->var.transp.length = 8;
		ltdc_handle[id].fb_info->var.red.offset = 16;
		ltdc_handle[id].fb_info->var.red.length = 8;
		ltdc_handle[id].fb_info->var.green.offset = 8;
		ltdc_handle[id].fb_info->var.green.length = 8;
		ltdc_handle[id].fb_info->var.blue.offset = 0;
		ltdc_handle[id].fb_info->var.blue.length = 8;
	}
	else
	{
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("Invalid pixel format\r\n");
		return -EINVAL;
	}
	//ID
	strcpy(ltdc_handle[id].fb_info->fix.id, "100ask_lcd");
	//显存大小和物理地址
	ltdc_handle[id].fb_info->fix.smem_len = ltdc_handle[id].fb_info->var.xres * ltdc_handle[id].fb_info->var.yres * ltdc_handle[id].fb_info->var.bits_per_pixel / 8;
	ltdc_handle[id].fb_info->fix.smem_start = ltdc_handle[id].fb_phy;
	//一行的显存长度
	ltdc_handle[id].fb_info->fix.line_length = ltdc_handle[id].fb_info->var.xres * ltdc_handle[id].fb_info->var.bits_per_pixel / 8;
	//显示器类型
	ltdc_handle[id].fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
	//像素格式
	ltdc_handle[id].fb_info->fix.visual = FB_VISUAL_TRUECOLOR;

	/* 注册 struct fb_info */
	result = register_framebuffer(ltdc_handle[id].fb_info);
	if(result < 0)
	{
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("register fb_info failed\r\n");
		return result;
	}

	//获取时钟
	ltdc_handle[id].pixel_clk = devm_clk_get(&pdev->dev, "lcd");
	if(!ltdc_handle[id].pixel_clk)
	{
		unregister_framebuffer(ltdc_handle[id].fb_info);
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("get pixel_clk failed\r\n");
		return -EINVAL;
	}
	//设置时钟速率
	clk_set_rate(ltdc_handle[id].pixel_clk, ltdc_handle[id].timing->pixelclock.typ);
	//使能时钟
	result = clk_prepare_enable(ltdc_handle[id].pixel_clk);
	if(result < 0)
	{
		unregister_framebuffer(ltdc_handle[id].fb_info);
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("enable pixel_clk failed\r\n");
		return result;
	}

	//获取GPIO号
	ltdc_handle[id].backlight = of_get_named_gpio(pdev->dev.of_node, "backlight", 0);
	if(ltdc_handle[id].backlight < 0)
	{
		clk_disable_unprepare(ltdc_handle[id].pixel_clk);
		unregister_framebuffer(ltdc_handle[id].fb_info);
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("get backlight failed\r\n");
		return ltdc_handle[id].backlight;
	}
	//申请GPIO
	result = gpio_request(ltdc_handle[id].backlight, "backlight");
	if(result < 0)
	{
		clk_disable_unprepare(ltdc_handle[id].pixel_clk);
		unregister_framebuffer(ltdc_handle[id].fb_info);
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("request backlight failed\r\n");
		return result;
	}
	//设置GPIO为输出
	gpio_direction_output(ltdc_handle[id].backlight, 0);

	//获取设备内存资源
	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if(!resource)
	{
		gpio_free(ltdc_handle[id].backlight);
		clk_disable_unprepare(ltdc_handle[id].pixel_clk);
		unregister_framebuffer(ltdc_handle[id].fb_info);
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("get resource failed\r\n");
		return -EIO;
	}
	//进行内存映射
	ltdc_handle[id].ltdc = devm_ioremap_resource(&pdev->dev, resource);
	if(!ltdc_handle[id].ltdc)
	{
		gpio_free(ltdc_handle[id].backlight);
		clk_disable_unprepare(ltdc_handle[id].pixel_clk);
		unregister_framebuffer(ltdc_handle[id].fb_info);
		framebuffer_release(ltdc_handle[id].fb_info);
		dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
		ltdc_handle[id].use_flag = 0;
		printk("map register failed\r\n");
		return -EIO;
	}
	//初始化控制器
	ltdc_init(ltdc_handle[id].ltdc, ltdc_handle[id].timing, ltdc_handle[id].bits_per_pixel, ltdc_handle[id].fb_phy);
	//使能控制器
	ltdc_enable(ltdc_handle[id].ltdc);

	//输出高电平，点亮LCD
	gpio_set_value(ltdc_handle[id].backlight, 1);

	return 0;
}

//设备或驱动卸载时执行
static int ltdc_remove(struct platform_device *pdev)
{
	uint32_t id;

	printk("%s\r\n", __FUNCTION__);

	id = pdev->id;
	if(!ltdc_handle[id].use_flag)
	{
		printk("verification failed\r\n");
		return -EINVAL;
	}

	//关闭屏幕
	gpio_set_value(ltdc_handle[id].backlight, 0);
	//禁止LTDC控制器
	ltdc_disable(ltdc_handle[id].ltdc);
	//释放GPIO
	gpio_free(ltdc_handle[id].backlight);
	//关闭时钟
	clk_disable_unprepare(ltdc_handle[id].pixel_clk);
	/* 注销 struct fb_info */
	unregister_framebuffer(ltdc_handle[id].fb_info);
	/* 释放 struct fb_info */
	framebuffer_release(ltdc_handle[id].fb_info);
	//释放显存
	dma_free_coherent(&pdev->dev, ltdc_handle[id].fb_size, ltdc_handle[id].fb_buffer, ltdc_handle[id].fb_phy);
	//释放LTDC句柄
	ltdc_handle[id].use_flag = 0;

	return 0;
}

static int __init ltdc_drv_init(void)
{
	int result;

	printk("%s\r\n", __FUNCTION__);

	memset(ltdc_handle, 0x00, sizeof(ltdc_handle));

	//注册平台驱动
	result = platform_driver_register(&ltdc_drv);
	if(result != 0)
		printk("add ltdc driver failed\r\n");
	return result;
}

static void __exit ltdc_drv_exit(void)
{
	printk("%s\r\n", __FUNCTION__);

	//注销平台驱动
	platform_driver_unregister(&ltdc_drv);
}

module_init(ltdc_drv_init);
module_exit(ltdc_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LF");
MODULE_DESCRIPTION("ltdc_drv");
